'''
二维码扫描代码
'''
import time
import cv2
import numpy as np
import pyzbar.pyzbar as pyzbar

import requests
import json

# 自己的包
import config


older_passager_cache = {}


'''
最终版，扫描二维码（乘客或者司机），上传到服务器验证，返回验证数据，显示

注：在扫描用户码时，树莓派只是单纯的将字符串发送到服务器，服务器负责把字符串解析成json报文或者是python对象。
    在扫描司机二维码时，是在树莓派端解析成json报文（因为乘客二维码的格式是我写的）

'''

def scan_code():
    font = cv2.FONT_HERSHEY_SIMPLEX # openCV 字体
    cap = cv2.VideoCapture(0)  # 0: use CSI camera,1：use USB camera
    cap.set(3,480) # set Width
    cap.set(4,360) # set Height
    if(not cap.isOpened()):
        print("[camera.py:cam]:can't open this camera")
   
    while(True):                                              # 循环不停扫码
        
        ret, img = cap.read()                                 # 读入图片
        img_ROI_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 转灰度
        barcodes = pyzbar.decode(img_ROI_gray)                # 解码，返回识别到的多个二维码对象的列表

        back_string = 'None'                                # 从服务器返回的字符串
     
        if len(barcodes) > 0:                               # 如果检测到二维码

            data_string = barcodes[0].data.decode("utf-8")  # 每次只选择一个二维码，并且解码,data_string是解码字符串

            (x,y,w,h) = barcodes[0].rect                    # 获取二维码坐标,坐标格式：[横坐标，纵坐标，长度，宽度]  单位：像素
            cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)  # 画出二维码的轮廓框
            cv2.imshow('image', img)                        # 显示轮廓
            print(data_string)

            if config.driver in data_string:                # 二维码类型分类，如果是司机二维码

                back_string = send_driver(data_string)                          # 调用send_driver函数，解析字符串，向服务器验证，同时返回验证信息
                cv2.putText(img, back_string , (20,100),font,1,(0,255,0),4)     # 打函数返回字符在图片上（可能是验证成功的字符，可能是验证失败的字符）
                cv2.imshow('image', img)                                        # 显示
                time.sleep(2)
            else:                                           # 二维码类型分类，如果是乘客二维码
                back_string = send_passager(data_string)
                cv2.putText(img, back_string , (20,100),font,1,(0,255,0),4)     # 打函数返回字符在图片上（可能是验证成功的字符，可能是验证失败的字符）
                cv2.imshow('image', img)                                        # 显示       
                time.sleep(2)                      
        else:                                               # 如果未检测到二维码
            cv2.imshow('image', img)
            time.sleep(0.3) # 隔0.3秒，读一次图片
        
        if (cv2.waitKey(1)) == ord('q'):
            break
    # 释放资源
    cap.release()
    cv2.destroyAllWindows() 


'''
function：
    将司机二维码的字符串解析成json报文，使用http协议发送json报文，接受服务器的验证信息，
    如果验证成功，返回 司机账号+ok，如果验证失败，返回字符串'None'
    如果验证成功，则可以初始化树莓派的车辆id和司机id（在config.py中）
    防止司机重复验证司机二维码

司机的二维码格式(字符串)：
    driver_id:xxxxxxxx,bus_id:xxxxxx
    driver_id是司机的工号，bus_id是车牌号

司机数据发送至服务器格式（json）：
    {'driver_id': 'xxxxxxxx', 'bus_id': 'xxxxxx'}

服务器返回的验证数据格式（json报文）：
    {'driver_id': 'xxxxxxxx', 'bus_id': 'xxxxxx', 'type': 'true/false'}

'''
def send_driver(str1):

    # 解析二维码字符串
    json_driver = {}
    attribute_list = str1.split(",")  # 切割字符串的每个属性
    if len(attribute_list) != 2:      # 如果属性数量不相符，则刷码失败
        return 'None'

    for attribute in attribute_list:  # 对每个属性，切割出其 key 和 value
        key_value_list = attribute.split(':')
        if len(key_value_list) != 2:
            return 'None'
        json_driver[ key_value_list[0] ] = key_value_list[1]  # 把每个 key 和 value 加入字典
    print(json_driver)

    if 'driver_id' not in json_driver:    # 字典内容检查
        return 'None'
    if 'bus_id' not in json_driver:       
        return 'None'

    if json_driver['bus_id'] == config.BUS_ID and json_driver['driver_id'] == config.DRIVER_ID:  # 重复扫码检查
        return config.DRIVER_ID + '  OK'

    response = requests.post(url=config.driver_URL, json=json_driver)  # 上传至服务器验证（阻塞）,同时接受返回回来的数据
    ret_type = json.loads(response.text).get('type')
    if ret_type == 'true':                                        # 判断数据是否验证成功
        #global config.BUS_ID  
        config.BUS_ID = json.loads(response.text).get('bus_id')   # 初始化树莓派需要绑定的信息
        #global config.DRIVER_ID
        config.DRIVER_ID = json.loads(response.text).get('driver_id')
        return config.DRIVER_ID + '  OK'
    return 'None'

    

'''
function:
    发送乘客的二维码（json），同时验证返回回来的数据，
    服务器：如果验证成功，返回乘客账号，如果验证失败，返回字符串'None'
    司机未扫码前，乘客不得扫码

乘客二维码格式：
    尚不清楚
发送至服务器格式（json）
    {'bus_id':'......', 'code':"...........二维码扫描到的字符串"}
从服务器接受的格式（json）：
    {'type':'true/false', 'passager_id':'xxxx','note':'二维码已扫描/二维码非法'}

'''
def send_passager(str2):

    if config.BUS_ID == config.blank and config.DRIVER_ID == config.blank:  # 如果树莓派没有初始化车辆身份信息，是不允许乘客扫码的
        return 'Uninitialized Car'

    json_passager = {}
    json_passager['bus_id'] = config.BUS_ID
    json_passager['code'] = str2
    response = requests.post(url=config.passage_URL, json=json_passager)  # 上传至服务器验证（阻塞）,同时接受返回回来的数据

    ret_type = json.loads(response.text).get('type')
    if ret_type == 'true':                                        # 判断数据是否验证成功
        passager_id = json.loads(response.text).get('passager_id')
        return passager_id + '  OK'
    else:
        note = json.loads(response.text).get('note')
        return 'Error: ' + note


# main函数
if __name__ == '__main__':
    '''
    str3= 'driver_id:1170300402,bus_id:黑A1234'
    ret = send_driver(str3)
    print(ret)
    '''
    scan_code()


'''
*****************  以下是无效代码  ************************
'''

# 扫描最大的二维码，上传云端，返回验证数据，显示
def get_code():
    # 该程序设计为一直运行，将扫描到的二维码上传云端，经过分析后，返回验证数据，来判断二维码是否合法

    font = cv2.FONT_HERSHEY_SIMPLEX # openCV 字体
    cap = cv2.VideoCapture(0)  # 0: use CSI camera,1：use USB camera
    cap.set(3,800) # set Width
    cap.set(4,600) # set Height
    if(not cap.isOpened()):
        print("[camera.py:cam]:can't open this camera")
    
    while(True):
        # 读入图片
        ret, img = cap.read() 
        # 转灰度
        img_ROI_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        # 解码
        barcodes = pyzbar.decode(img_ROI_gray) # 返回识别到的多个二维码对象的列表

        area = 0 # 面积
        max_barcode = None # 最大的二维码
        # 选出面积最大的二维码
        for barcode in barcodes:
            (x,y,w,h) = barcode.rect  # 坐标格式：[横坐标，纵坐标，长度，宽度]  单位：像素
            if (area <= w*h):
                area = w*h
                max_barcode = barcode
        # 处理最大的二维码信息
        if (max_barcode != None):

            # 获取二维码坐标并且绘制
            (x,y,w,h) = max_barcode.rect  # 坐标格式：[横坐标，纵坐标，长度，宽度]  单位：像素
            cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)
            # 显示二维码区域
            cv2.imshow('image', img)

            # 解析二维码数据：       条形码数据为字节对象，所以如果我们想在输出图像上画出来，就需要先将它转换成字符串
            barcodeData = max_barcode.data.decode("utf-8")
            barcodeType = max_barcode.type
            text = "{} ({})".format(barcodeData, barcodeType)

            # 向后端发送二维码数据，并且等待收回验证（阻塞函数？）


            # 验证是否合法二维码

            # 打印字符在图片上
            cv2.putText(img, text , (20,100),font,1,(0,255,0),4) 
            # 向终端打印条形码数据和条形码类型
            print("[INFO] Found {} barcode: {}".format(barcodeType, barcodeData))

        # 显示
        cv2.imshow('image', img)
        if (cv2.waitKey(1)) == ord('q'):
            break
        time.sleep(0.5) # 隔一秒，读一次图片
    # 释放资源
    cap.release()
    cv2.destroyAllWindows() 